package com.zxd.interview.netty.netty.serverclientstart;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.util.AttributeKey;

/**
 * netty 服务端启动模板嗲吗
 * 源码分析的代码略有不同
 * 目标：使用Netty实现自动绑定递增端口
 */
public class NettyServerStart {

    public static void main(String[] args) {
        ServerBootstrap serverBootstrap = new ServerBootstrap();

        NioEventLoopGroup boss = new NioEventLoopGroup();
        NioEventLoopGroup worker = new NioEventLoopGroup();
        int port = 10022;
        serverBootstrap
                .group(boss, worker)
                .channel(NioServerSocketChannel.class)
                .handler(new ChannelInitializer() {
                    @Override
                    protected void initChannel(Channel ch) throws Exception {
                        // 指定服务端启动过程的一些逻辑
                        System.out.println("服务端启动当中");
                    }
                })

                // 指定自定义属性，客户端可以根据此属性进行一些判断处理
                // 可以看作给Channel维护一个Map属性，这里的channel是服务端
                // 允许指定一个新创建的通道的初始属性。如果该值为空，指定键的属性将被删除。
                .attr(AttributeKey.newInstance("hello"), "hello world")

                // 给每个连接指定自定义属性，Channel 进行属性指定等
                // 用给定的值在每个 子通道 上设置特定的AttributeKey。如果该值为空，则AttributeKey将被删除。
                // 区别是是否是 子channel，子Channel代表给客户端的连接设置
                .childAttr(AttributeKey.newInstance("childAttr"), "childAttr")

                // 客户端的 Channel 设置TCP 参数
                // so_backlog 临时存放已完成三次握手的请求队列的最大长度，如果频繁连接可以调大此参数
                .option(ChannelOption.SO_BACKLOG, 1024)

                // 给每个连接设置TCP参数
                // tcp的心跳检测，true为开启
                .childOption(ChannelOption.SO_KEEPALIVE, true)
                // nagle 算法开关，实时性要求高就关闭
                .childOption(ChannelOption.TCP_NODELAY, true)

                .childHandler(new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    protected void initChannel(NioSocketChannel ch) throws Exception {
                        ch.pipeline().addLast(new StringDecoder());
                        ch.pipeline().addLast(new SimpleChannelInboundHandler<String>() {
                            @Override
                            protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
                                System.err.println(msg);
                            }
                        });

                    }

                });
        bind(serverBootstrap, port);
    }

    /**
     * 自动绑定递增端口
     * @param serverBootstrap
     * @param port
     */
    public static void bind(ServerBootstrap serverBootstrap, int port){
        serverBootstrap.bind(port).addListener(future -> {
            if(future.isSuccess()){
                System.out.println("端口绑定成功");
                System.out.println("绑定端口"+ port +"成功");
            }else{
                System.out.println("端口绑定失败");
                bind(serverBootstrap, port+1);
            }
        });
    }
}
